#include "zookeeperutil.h"
#include "mprpcapplication.h"
#include <semaphore.h>
#include <iostream>


// 全局的watcher观察器 zkserver给client的通知
void globle_watcher(zhandle_t *zh, int type,
                    int state, const char *path,
                    void *watcherCtx)
{
    if (type == ZOO_SESSION_EVENT) // 回调消息类型是和会话相关的消息类型
    {
        if (state == ZOO_CONNECTED_STATE) // zkclient和zkserver连接成功
        {
            sem_t *sem = (sem_t *)zoo_get_context(zh); // 获取信号量
            sem_post(sem);                             // 信号量资源加一
        }
    }
}
zkClient::zkClient()
    : m_zhandle(nullptr)
{}
zkClient::~zkClient()
{
    if (m_zhandle != nullptr)
    {
        zookeeper_close(m_zhandle); // 关闭句柄，释放锁资源
    }
}
void zkClient::Start()
{
    std::string host = MprpcApplication::GetInstance().GetConfig().Load("zookeeperip");
    std::string port = MprpcApplication::GetInstance().GetConfig().Load("zookeeperport"); 
    
    std::string connstr = host + ":" + port; // 给定了固定的格式 host:port
    std::cout << "Connecting to: " << connstr << std::endl;

    /*
    zookeeper_mt多线程版本，即它的API客户端程序提供了三个线程,是异步的额
    API调用线程
    网络i/o线程 是在zookeeper_init接口中使用pthread_create创建的线程 客户端程序不需要网络性能所以是poll模型
    watcher回调线程
    */
    // zookeeper_init是zookeeper中自带的接口，30000是会话的超时时间
   
    m_zhandle = zookeeper_init(connstr.c_str(), globle_watcher, 30000, nullptr, nullptr, 0);
    if (nullptr == m_zhandle)
    {
        std::cout << "zookeeper_init error" << std::endl;
        exit(EXIT_FAILURE);
    }
    
    sem_t sem;
    sem_init(&sem, 0, 0);
    zoo_set_context(m_zhandle, &sem); // 绑定信号量，只有当上面的watcher那里信号量加一这里才能继续向下走
    sem_wait(&sem);
    std::cout << "zookeeper_init success!" << std::endl;
}
void zkClient::Create(const char *path, const char *data, int datalen, int state)
{
    char path_buffer[128];
    int buffer_len = sizeof(path_buffer);
    int flag;
    // 先判断想要创建的节点是否存在，如果在就不要重复创建
    flag = zoo_exists(m_zhandle, path, 0, nullptr);
    if (ZNONODE == flag) // 需要创建的节点不存在
    {
        flag = zoo_create(m_zhandle, path, data, datalen,
                          &ZOO_OPEN_ACL_UNSAFE, state, path_buffer, buffer_len);
        if (flag == ZOK)
        {
            std::cout << "znode create success... path:" << path << std::endl;
        }
        else
        {
            std::cout << "flag:" << flag << std::endl;
            std::cout << "znode create error... path" << path << std::endl;
            exit(EXIT_FAILURE);
        }
    }
}
std::string zkClient::GetData(const char *path)
{
    char buffer[64];
    int buffer_len = sizeof(buffer);
    int flag = zoo_get(m_zhandle, path, 0, buffer, &buffer_len, nullptr);
    if (flag != ZOK) // ZOK表示操作成功
    {
        std::cout << "Get znode error... path:" << path << std::endl;
        return "";
    }
    else
    {
        return buffer;
    }
}
